;;; Mudsync --- Live hackable MUD
;;; Copyright © 2016 Christopher Allan Webber <cwebber@dustycloud.org>
;;;
;;; This file is part of Mudsync.
;;;
;;; Mudsync is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; Mudsync is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
;;; General Public License for more details.
;;;
;;; You should have received a copy of the GNU General Public License
;;; along with Mudsync.  If not, see <http://www.gnu.org/licenses/>.

(define-module (mudsync run-game)
  #:use-module (mudsync game-master)
  #:use-module (8sync agenda)
  #:use-module (8sync repl)
  #:use-module (8sync systems actors)
  #:use-module (8sync systems actors debug)
  #:use-module (srfi srfi-1)
  #:use-module (ice-9 receive)
  #:use-module (ice-9 q)
  #:export (run-demo
            do-inject-special!
            make-special-injector

            ;; Debug stuff, might go away
            %live-gm %live-hive
            inject-gameobj!))


;;; Debugging stuff
;;; ===============

;; @@: Could these be parameterized and this still work?
(define %live-gm #f)
(define %live-hive #f)

;; Evil!  This uses a global variable... but it's hard to give any more
;; convenient way of providing something for live hacking (which is
;; "quasi-evil for productivity's sake" anyway).  You can set up your own
;; solution which doesn't use a global though.

(define (inject-gameobj! game-spec special-symbol)
  (display "Game hasn't been started...\n"))



;;; Game running stuff
;;; ==================

(define* (run-demo game-spec default-room #:key repl-server)
  (define hive (make-hive))
  (define new-conn-handler
    (make-default-room-conn-handler default-room))
  (define gm
    (hive-create-actor-gimmie* hive <game-master> "gm"
                               #:new-conn-handler new-conn-handler))
  (define initial-tasks
    (list (bootstrap-message hive (actor-id gm) 'init-world
                             #:game-spec game-spec)))
  (define agenda
    (make-agenda #:pre-unwind-handler print-error-and-continue
                 #:queue (list->q initial-tasks)))

  (set! %live-gm gm)
  (set! %live-hive hive)
  (receive (post-run-hook gameobj-injector)
      (make-special-injector agenda hive (actor-id gm))
    ;; Set up injector for live hacking
    (set! inject-gameobj! gameobj-injector)

    ;; Set up REPL sever
    (cond
     ;; If repl-server is an integer, we'll use that as the port
     ((integer? repl-server)
      (spawn-and-queue-repl-server! agenda repl-server))
     (repl-server
      (spawn-and-queue-repl-server! agenda)))

    (start-agenda agenda
                  #:post-run-hook post-run-hook)))


(define (do-inject-special! queue hive gm-id game-spec special-symbol)
  (define gameobj-spec
    (or (find
         (lambda (entry) (eq? (car entry) special-symbol))
         game-spec)
        (throw 'no-such-symbol "Can't find such a symbol in the game-spec"
               #:symbol special-symbol)))
  (define task
    (bootstrap-message hive gm-id 'inject-special!
                       #:special-symbol special-symbol
                       #:gameobj-spec gameobj-spec))
  (enq! queue task)
  'done)

(define (queue-injected-tasks-on-agenda! agenda inject-queue)
  "Inject tasks from the inject-queue onto the agenda queue."
  (while (not (q-empty? inject-queue))
    (enq! (agenda-queue agenda) (q-pop! inject-queue))))

(define* (make-special-injector agenda hive gm-id)
  "Make a post-run-hook and gameobj injector for quick live hacking."
  (define inject-queue (make-q))
  (values
   (lambda (agenda)
     (queue-injected-tasks-on-agenda! agenda inject-queue))
   (lambda (game-spec special-symbol)
     (do-inject-special! inject-queue hive gm-id
                         game-spec special-symbol))))
